﻿// http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace HttpServer
{
    interface IStep
    {
        void Work(StateContext context);
    }

    internal class Step0 : IStep
    {
        public void Work(StateContext context)
        {
            var encoder = new ASCIIEncoding();

            //const string responseStr = "HTTP/1.1 407 Proxy Authentication Required" +
            //                            "\r\nVia: 1.1 WARGTY01" +
            //                            "\r\nProxy-Authenticate: Negotiate" +
            //                            "\r\nProxy-Authenticate: Kerberos" +
            //                            "\r\nProxy-Authenticate: NTLM" +
            //                            "\r\nConnection: Keep-Alive" +
            //                            "\r\nProxy-Connection: Keep-Alive" +
            //                            "\r\nPragma: no-cache" +
            //                            "\r\nCache-Control: no-cache" +
            //                            "\r\nContent-Type: text/html" +
            //                            "\r\nContent-Length: 13" +
            //                            "\r\n\r\n" +
            //                            "<HTML></HTML>";


            const string responseStr = "HTTP/1.1 407 Proxy Authentication Required" +
                                        "\r\nVia: 1.1 WARGTY01" +
                                        "\r\nProxy-Authenticate: Kerberos" +
                                        "\r\nProxy-Authenticate: NTLM" +
                                        "\r\nConnection: Keep-Alive" +
                                        "\r\nProxy-Connection: Keep-Alive" +
                                        "\r\nPragma: no-cache" +
                                        "\r\nCache-Control: no-cache" +
                                        "\r\nContent-Type: text/html" +
                                        "\r\nContent-Length: 13" +
                                        "\r\n\r\n" +
                                        "<HTML></HTML>";

            Console.WriteLine(responseStr);

            var buffer = encoder.GetBytes(responseStr);

            //HTTP/1.1 407 Proxy Authentication Required ( Forefront TMG requires authorization to fulfill the request. Access to the Web Proxy filter is denied.  )

            //Via: 1.1 WARGTY01
            //Proxy-Authenticate: Negotiate
            //Proxy-Authenticate: Kerberos
            //Proxy-Authenticate: NTLM
            //Connection: Keep-Alive
            //Proxy-Connection: Keep-Alive
            //Pragma: no-cache
            //Cache-Control: no-cache
            //Content-Type: text/html
            //Content-Length: 13

            //<HTML></HTML>

            context.ClientStream.Write(buffer, 0, buffer.Length);
            context.ClientStream.Flush();

            context.SetState(new Step1());
        }
    }

    internal class Step1 : IStep
    {
        public void Work(StateContext context)
        {
            var encoder = new ASCIIEncoding();

            const string responseStr = "HTTP/1.1 407 Proxy Authentication Required\r\nVia: 1.1 WARGTY01\r\nProxy-Authenticate: Negotiate TlRMTVNTUAACAAAADAAMADgAAAAVgonilcj4Ud4XZCoAAAAAAAAAAJoAmgBEAAAABgGxHQAAAA9DAEQAUwAtAEIAQQACAAwAQwBEAFMALQBCAEEAAQAQAFcAQQBSAEcAVABZADAAMQAEABgAYwBkAHMALQBiAGEALgBsAG8AYwBhAGwAAwAqAFcAQQBSAEcAVABZADAAMQAuAGMAZABzAC0AYgBhAC4AbABvAGMAYQBsAAUAGABjAGQAcwAtAGIAYQAuAGwAbwBjAGEAbAAHAAgAx6Se+fGBzAEAAAAA\r\nConnection: Keep-Alive\r\nProxy-Connection: Keep-Alive\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n";
            Console.WriteLine(responseStr);

            var buffer = encoder.GetBytes(responseStr);
            context.ClientStream.Write(buffer, 0, buffer.Length);
            context.ClientStream.Flush();

            context.SetState(new Step2());
        }
    }

    internal class Step2 : IStep
    {
        public void Work(StateContext context)
        {
            var encoder = new ASCIIEncoding();

            const string responseStr = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nHola";
            Console.WriteLine(responseStr);

            var buffer = encoder.GetBytes(responseStr);
            context.ClientStream.Write(buffer, 0, buffer.Length);
            context.ClientStream.Flush();

            context.SetState(null);
        }
    }

    internal class StateContext
    {
        private IStep _nextStep;
        private NetworkStream _clientStream;

        public StateContext()
        {
            _nextStep = new Step0();
        }

        public void SetState( IStep step )
        {
            _nextStep = step;
        }

        public NetworkStream ClientStream
        {
            get { return _clientStream; }
        }

        public void Work(NetworkStream clientStream)
        {
            _clientStream = clientStream;

            if (_nextStep != null)
            {
                _nextStep.Work(this);
            }
        }
    }


    class Server
    {
        private readonly TcpListener _tcpListener;
        private readonly Thread _listenThread;
        private bool _stoped;
        private readonly StateContext _stateContext = new StateContext();

        public Server( int port )
        {
            this._tcpListener = new TcpListener(IPAddress.Any, port);
            this._listenThread = new Thread(new ThreadStart(ListenForClients));
            this._listenThread.Start();
        }

        public void Stop()
        {
            _stoped = true;

            this._listenThread.Join();
        }

        private void ListenForClients()
        {
            this._tcpListener.Start();

            while (!_stoped)
            {
                var client = this._tcpListener.AcceptTcpClient();
                var clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }



        private void HandleClientComm(object client)
        {
            var tcpClient = (TcpClient)client;
            var clientStream = tcpClient.GetStream();

            var message = new byte[4096];

            while (true)
            {
                int bytesRead = 0;

                try
                {
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    break;
                }

                if (bytesRead == 0)
                {
                    break;
                }

                var encoder = new ASCIIEncoding();
                
                string str = encoder.GetString(message, 0, bytesRead);
                //System.Diagnostics.Debug.WriteLine(str);
                Console.WriteLine("-----------Received: ...");
                Console.WriteLine(str);

                Console.WriteLine("press ENTER to continue...");
                Console.ReadLine();


                var xxx = str.IndexOf("favicon.ico");

                if (xxx < 0)
                {
                    Console.WriteLine("-----------Send: ...");
                    DoResponse(tcpClient);

                    Console.WriteLine("press ENTER to continue...");
                    Console.ReadLine();
                }

                

            } //while (false);



            //tcpClient.Close();
        }

        private void DoResponse( TcpClient tcpClient )
        {
            _stateContext.Work(tcpClient.GetStream());
        }

    }






    class Program
    {

        private static bool _keepRunning = true;

        static void Main(string[] args)
        {
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;
                Program._keepRunning = false;
            };

            
            var server = new Server(9090);


            while (Program._keepRunning)
            {
                Thread.Sleep(500);
            }

            Console.WriteLine("Stoping...");
            server.Stop();

            Console.WriteLine("Stoped. Press ENTER to exit.");
            Console.ReadLine();
        }
    }
}
